﻿using Core.Infarastructure;
using Core.Infarastructure.Structs;
using Core.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;

namespace Core.Logic
{
    /// <summary>
    /// Provides the main logic of the game
    /// </summary>
    public class Engine
    {
        private List<Stack> _stacks;

        private List<Stack> sortedStacks;
        private Hero _hero;
        private DrawingFieldStructure _drawingField = new DrawingFieldStructure();
        private int _level = 1;
       
        private List<Stack> fallingStacks;

        
        public DrawingFieldStructure DrawingField
        {
            get
            {
                return _drawingField;
            }
            set
            {
                _drawingField = value;
            }
        }


        public Engine(List<Stack> stacks, Hero hero)
        {
            _stacks = stacks;
            sortedStacks = new List<Stack>(_stacks);
            sortedStacks.Sort(CoordinatesComparerX);
            _hero = hero;
            Initializator();
            //
            fallingStacks = new List<Stack>();
        }

        /// <summary>
        /// Get/Set hero coordinates
        /// </summary>
        public Core.Infarastructure.Structs.Point HeroPosition
        {
            get
            {
                return new Point() 
                {
                    X = _hero.CurrentPosition_X,
                    Y = _hero.CurrentPosition_Y
                };
            }
        }

        private void Initializator()
        {
            Random r = new Random();
            _stacks.Add(new Stack(Constants.STACK_SIZE * r.Next(0, 11), Constants.Y_POSITION_OF_DRAWING_FIELD));
        }
        /// <summary>
        /// Checks if hero and stacks intersect the drawing field
        /// </summary>
        public void CheckBorders()
        {
            if (this._hero.CurrentPosition_X == _drawingField.Width-Constants.STACK_SIZE)
            {
                this._hero.CantGoRight = true;
            }
            if (this._hero.CurrentPosition_X == Constants.X_POSITION_OF_DRAWING_FIELD)
            {
                this._hero.CantGoLeft = true;
            }

            foreach (var stack in _stacks)
            {
                //if (this._hero.CurrentPosition_X == stack.XPosition - Constants.STACK_SIZE && stack.Fixed
                //    && _hero.CurrentPosition_Y == stack.YPosition)
                //{
                //    this._hero.CantGoRight = true;

                //}
                if (this._hero.CurrentPosition_X == stack.XPosition - Constants.STACK_SIZE && stack.Fixed
                    && Math.Abs(_hero.CurrentPosition_Y - stack.YPosition) < Constants.STACK_SIZE)
                {
                    this._hero.CantGoRight = true;

                }

                //if (this._hero.CurrentPosition_X - Constants.STACK_SIZE == stack.XPosition && stack.Fixed
                //    && _hero.CurrentPosition_Y == stack.YPosition)
                //{
                //    this._hero.CantGoLeft = true;
                //}

                if (this._hero.CurrentPosition_X - Constants.STACK_SIZE == stack.XPosition && stack.Fixed
                    && Math.Abs(_hero.CurrentPosition_Y - stack.YPosition) < Constants.STACK_SIZE)
                {
                    this._hero.CantGoLeft = true;
                }
            }
        }

        /// <summary>
        /// Fix stacks that are located near borders
        /// </summary>
        public void FixStacksNearBorders()
        {
            foreach (var currentStack in _stacks)
            {
                if (currentStack.Fixed)
                {
                    continue;
                }
                if (currentStack.XPosition == Constants.X_POSITION_OF_DRAWING_FIELD || 
                    currentStack.XPosition == this._drawingField.Width - Constants.STACK_SIZE)
                {
                    currentStack.Fixed = true;
                }
            }


        }

        /// <summary>
        /// Checks if stack intersect the drawing field
        /// </summary>
        /// <param name="st"></param>
        /// <returns></returns>
        private bool CheckBorderForStack(Stack st)
        {
            if (this._drawingField.Height - Constants.STACK_SIZE < st.YPosition)
            {
                st.YPosition = this._drawingField.Height - Constants.STACK_SIZE;
                return true;
            }
            else
            {
                return false;
            }

        }

        /// <summary>
        /// Checks if stack that is falling intersect other stacks
        /// </summary>
        /// <param name="st"></param>
        /// <returns></returns>
        private bool CheckStackOnStack(Stack st, bool falling = false)
        {
            foreach (var currentStack in _stacks)
            {
                
                if (currentStack.XPosition == st.XPosition && currentStack.YPosition == st.YPosition)
                {
                    continue;
                }
                if (falling)
                {
                    if (currentStack.XPosition == st.XPosition 
                        && st.YPosition - currentStack.YPosition > -Constants.STACK_SIZE
                        && Math.Abs(st.YPosition - currentStack.YPosition) <= Constants.STACK_SIZE)
                    {
                        st.YPosition = currentStack.YPosition + Constants.STACK_SIZE;
                        return true;

                    }
                }
                else
                {
                    
                    if (currentStack.YPosition - Constants.STACK_SIZE == st.YPosition
                        && Math.Abs(currentStack.XPosition - st.XPosition) < (int)(Constants.STACK_SIZE / 2))
                    {
                        return true;
                    } 
                }
                
            }
            return false;
        }

        /// <summary>
        /// Checks if stack that is falling intersect the hero
        /// </summary>
        /// <param name="st"></param>
        /// <returns></returns>
        public bool CheckStackOnHero(Stack st)
        {
            if (Math.Abs(this._hero.CurrentPosition_Y - st.YPosition) < Constants.STACK_SIZE //(this._hero.CurrentPosition_Y - Constants.STACK_SIZE < st.YPosition) 
                && Math.Abs(this._hero.CurrentPosition_X - st.XPosition) < Constants.STACK_SIZE 
                && !_hero.Jumping)
            {
                _hero.Killed = true;
                return true;
            }

            if ((this._hero.CurrentPosition_Y - Constants.STACK_SIZE < st.YPosition)
                && Math.Abs(this._hero.CurrentPosition_X - st.XPosition) < Constants.STACK_SIZE && _hero.Jumping)
            {
                _hero.DestroyStackInAir = true;
                _hero.Score += 15;
                _stacks.Remove(st);
                sortedStacks.Remove(st);
                return true;
            }
           

            return false;
        }


        /// <summary>
        /// Checks is the free space under hero on right
        /// </summary>
        /// <returns></returns>
        public bool IsFreeSpaceUnderHeroOnRight()
        {
            if (_hero.RightStack != null && !_hero.RightStack.Falling)
            {
               
                if (_hero.RightStack.YPosition < DrawingField.Height - Constants.STACK_SIZE)
                {
                    foreach (var currentStack in _stacks)
                    {

                        if (Math.Abs(currentStack.XPosition - _hero.RightStack.XPosition) < Constants.STACK_SIZE
                            && !currentStack.Falling)
                        {
                            if (Math.Abs(currentStack.YPosition - _hero.RightStack.YPosition) == 25)
                            {
                                return false;
                            }
                        }
                    }
                    return true;
                   
                }
            }
            return false;
        }

        /// <summary>
        /// Checks is the free space under hero on left
        /// </summary>
        /// <returns></returns>
        public bool IsFreeSpaceUnderHeroOnLeft()
        {
            if (_hero.LeftStack != null && !_hero.LeftStack.Falling)
            {

                if (_hero.LeftStack.YPosition < DrawingField.Height - Constants.STACK_SIZE)
                {
                    foreach (var currentStack in _stacks)
                    {

                        if (Math.Abs(currentStack.XPosition - _hero.LeftStack.XPosition) < Constants.STACK_SIZE
                            && !currentStack.Falling)
                        {
                            if (Math.Abs(currentStack.YPosition - _hero.LeftStack.YPosition) == 25)
                            {
                                return false;
                            }
                        }
                    }
                    return true;

                }
            }
            return false;
        }
        //
        public void IsFreeSpaceUnderHero()
        {
            if (_hero.CurrentPosition_Y < DrawingField.Height - Constants.STACK_SIZE && !_hero.IsMoving)
            {
                foreach (var currentStack in _stacks)
                {
                    if (_hero.CurrentPosition_X == currentStack.XPosition)
                    {
                        if (Math.Abs(_hero.CurrentPosition_Y - currentStack.YPosition) == Constants.STACK_SIZE)
                        {
                            _hero.Fall = false;
                            return;
                        }
                    }

                    
                }

                if ((DrawingField.Height - _hero.CurrentPosition_Y) % Constants.STACK_SIZE == 0 
                    && !_hero.GoBack)
                {
                    _hero.Fall = true;
                }
                
            }

           
        }
        /// <summary>
        /// Checks the right stack that is slided by hero if is free space under it, if not, 
        /// add it to collection of stacks that must fall down
        /// </summary>
        public void CheckFreeSpaceUnderRightStack()
        {
            if (IsFreeSpaceUnderHeroOnRight())
	        {
                fallingStacks.Add(_hero.RightStack);
            }
              

        }
        /// <summary>
        /// Checks the left stack that is slided by hero if is free space under it, if not, 
        /// add it to collection of stacks that must fall down
        /// </summary>
        public void CheckFreeSpaceUnderLeftStack()
        {
            if (IsFreeSpaceUnderHeroOnLeft())
            {
                fallingStacks.Add(_hero.LeftStack);
            }

        }



        public void CheckIfNotFixed()
        {
            for (int i = 0; i < _stacks.Count; i++)
            {
                if (!_stacks[i].Fixed)
                {
                    continue;
                }
                for (int j = 1; j < _stacks.Count; j++)
                {
                    if (Math.Abs(_stacks[i].XPosition - _stacks[j].XPosition)> Constants.STACK_SIZE
                        && _stacks[i].YPosition - _stacks[j].YPosition < -Constants.STACK_SIZE)
                    {
                        _stacks[i].Fixed = false;
                    }
                }
            }
        }

        /// <summary>
        /// checks the opportunity of hero moving and unlock it's movements if it is possible 
        /// </summary>
        public void CheckIsFreeRouteLeft()
        {
            if (!_hero.CantGoLeft)
            {
                return;
            }
            if (_hero.LeftStack == null && !_hero.IsMoving)
            {
                _hero.CantGoLeft = false;
            }
           
        }

        /// <summary>
        /// checks the opportunity of hero moving and unlock it's movements if it is possible 
        /// </summary>
        public void CheckIsFreeRouteRight()
        {
            if (!_hero.CantGoRight)
            {
                return;
            }
            if (_hero.RightStack == null && !_hero.IsMoving)
            {
                _hero.CantGoRight = false;
            }
        }
       /// <summary>
       /// Fix stacks that are under another stacks
       /// </summary>
        public void FixLowerStacks()
        {
            for (int i = 0; i < _stacks.Count; i++)
            {
                for (int j = 1; j < _stacks.Count; j++)
                {
                    if (_stacks[i].XPosition == _stacks[j].XPosition
                    && Math.Abs(_stacks[i].YPosition - _stacks[j].YPosition) == Constants.STACK_SIZE)
                    {
                        if (_stacks[i].YPosition < _stacks[j].YPosition)
                        {
                            _stacks[j].Fixed = true;
                        }
                        else
                        {
                            _stacks[i].Fixed = true;
                        }

                    }

                }
            }
        }


        /// <summary>
        /// Fix stacks that is next to stack is sliding by hero
        /// </summary>
        public void NearHeroStacks()
        {
            for (int i = 0; i < _stacks.Count; i++)
            {
                if (_hero.RightStack!=null 
                    && Math.Abs(_hero.RightStack.XPosition - _stacks[i].XPosition) == Constants.STACK_SIZE
                    && !_stacks[i].Falling
                    && _hero.RightStack.YPosition == _stacks[i].YPosition)//addded
                {
                    _hero.CantGoRight = true;
                    _hero.RightStack.Fixed = true;
                    _stacks[i].Fixed = true;
                }
                if (_hero.LeftStack != null 
                    && Math.Abs(_hero.LeftStack.XPosition - _stacks[i].XPosition) == Constants.STACK_SIZE 
                    && !_stacks[i].Falling
                    && _hero.LeftStack.YPosition == _stacks[i].YPosition)//added
                {
                    _hero.CantGoLeft = true;
                    _hero.LeftStack.Fixed = true;
                    _stacks[i].Fixed = true;
                }
            }
        }

        /// <summary>
        /// Fix the stacks that are near each other
        /// </summary>
        public void CheckSideBySideStacks()
        {
            
            int indexOfFallingStack = sortedStacks.IndexOf(_stacks.Last());
            if (indexOfFallingStack != sortedStacks.Count - 1
                && indexOfFallingStack > 0)
            {
                if (_stacks.Last().XPosition - sortedStacks[indexOfFallingStack - 1].XPosition == Constants.STACK_SIZE
                    && _stacks.Last().YPosition - sortedStacks[indexOfFallingStack - 1].YPosition < Constants.STACK_SIZE)
                {
                    _stacks.Last().Fixed = true;
                }

                if (sortedStacks[indexOfFallingStack + 1].XPosition - _stacks.Last().XPosition == Constants.STACK_SIZE
                    && _stacks.Last().YPosition - sortedStacks[indexOfFallingStack + 1].YPosition < Constants.STACK_SIZE)
                {
                    _stacks.Last().Fixed = true;
                }
            }


            for (int i = _stacks.Count - 1; i >=0; i--)
            {
                for (int j = _stacks.Count-2; j > 1 ; j--)
                {
                    if (Math.Abs(_stacks[i].XPosition - _stacks[j].XPosition) == Constants.STACK_SIZE
                    && Math.Abs(_stacks[i].YPosition - _stacks[j].YPosition) < Constants.STACK_SIZE)
                    {
                        _stacks[i].Fixed = true;
                        _stacks[j].Fixed = true;

                    }
                }
            }

           
            
        }


        /// <summary>
        /// Generates falling stacks
        /// </summary>
        /// <param name="st"></param>
        public void StackFalling()
        {
                if (!CheckBorderForStack(_stacks.Last())
                    && !CheckStackOnStack(_stacks.Last()) 
                    && !CheckStackOnHero(_stacks.Last()))
                {
                    
                    for (int j = 0; j < fallingStacks.Count; j++)
                    {
                        
                        if (!CheckStackOnStack(fallingStacks[j], fallingStacks.Count>1 ? true:false) 
                            && !CheckBorderForStack(fallingStacks[j]) 
                            && !fallingStacks[j].Falling)
                        {
                            fallingStacks[j].YPosition++;
                        }
                        else
                        {
                            fallingStacks.Remove(fallingStacks[j]);
                            j = -1;
                        }
                    }



                    _stacks.Last().YPosition += Stack.Speed;
                }
                else
                {
                    sortedStacks.Add(_stacks.Last());
                    _stacks.Last().Falling = false;
                    Random r = new Random();
                    _stacks.Add(new Stack(Constants.STACK_SIZE * r.Next(0, 11), 
                        Constants.Y_POSITION_OF_DRAWING_FIELD));
                }
        }

        
        /// <summary>
        /// Set to hero RightStack property first right stack which is crossed by hero
        /// </summary>
        /// <returns></returns>
        public Stack CheckRouteRight()
        {
            foreach (var currentStack in _stacks)
            {
                if (currentStack.Falling)
                {
                    if (_hero.CurrentPosition_X + Constants.STACK_SIZE == currentStack.XPosition
                    && _hero.CurrentPosition_Y < currentStack.YPosition + Constants.STACK_SIZE)
                    {
                        _hero.RightStack = currentStack;
                        return _hero.RightStack;
                    }
                }
                else
                {
                    if (_hero.CurrentPosition_X + Constants.STACK_SIZE == currentStack.XPosition
                    && _hero.CurrentPosition_Y == currentStack.YPosition)
                    {
                        _hero.RightStack = currentStack;
                        return _hero.RightStack;
                    }
                }
                
            }
            _hero.RightStack = null;
            return null;
        }


        /// <summary>
        /// Set to hero LeftStack property first right stack which is crossed by hero
        /// </summary>
        /// <returns></returns>
        public Stack CheckRouteLeft()
        {
            foreach (var currentStack in _stacks)
            {
                if (currentStack.Falling)
                {
                    if (_hero.CurrentPosition_X == currentStack.XPosition + Constants.STACK_SIZE
                    && _hero.CurrentPosition_Y < currentStack.YPosition + Constants.STACK_SIZE)
                    {
                        _hero.LeftStack = currentStack;
                        return _hero.LeftStack;
                    }
                }
                else
                {
                    if (_hero.CurrentPosition_X == currentStack.XPosition + Constants.STACK_SIZE 
                        && _hero.CurrentPosition_Y == currentStack.YPosition)
                    {
                        _hero.LeftStack = currentStack;
                        return _hero.LeftStack;
                    }
                }
                
            }
            _hero.LeftStack = null;
            return null;
        }


        /// <summary>
        /// Checks the bottom stacks row
        /// </summary>
        /// <returns></returns>
        public bool CheckLines()
        {
            var disappearPretendents = _stacks.Select(s => s).Where(s => DrawingField.Height - s.YPosition == Constants.STACK_SIZE).ToList();
            if (disappearPretendents.Count()==Constants.STACKS_COUNT_IN_GORIZONTAL_LINE)
            {
                for (int i = 0; i < disappearPretendents.Count; i++)
                {
                    _stacks.Remove(disappearPretendents[i]);
                    sortedStacks.Remove(disappearPretendents[i]);
                }
                fallingStacks.Clear();
                for (int i = 0; i < _stacks.Count; i++)
                {
                    
                    if (!_stacks[i].Falling)
                    {
                        _stacks[i].Fixed = false;
                        fallingStacks.Add(_stacks[i]);
                    }
                    
                }
                fallingStacks.Sort(CoordinatesComparerY);
                _level++;
                _hero.Score += 100;
                return true;
                
            }
            return false;
        }
        
        
        

        private static int CoordinatesComparerX(Stack x, Stack y)
        {
            if (x.XPosition < y.XPosition)
            {
                return -1;
            }
            if (x.XPosition == y.XPosition)
            {
                return 0;
            }
            else
            {
                return 1;
            }
        }

        private static int CoordinatesComparerY(Stack x, Stack y)
        {
            if (x.YPosition > y.YPosition)
            {
                return -1;
            }
            if (x.YPosition == y.YPosition)
            {
                return 0;
            }
            else
            {
                return 1;
            }
        }

       

      
    }
}
